package api

import (
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"guard/internal/app/oauth/models"
	"guard/internal/conf"
	"guard/internal/errors"
	"net/http"
	"strings"
)

// Authorize Get请求返回HTML和client数据，post用于授权数据整理
func authorize(c *gin.Context) {

	method := c.Request.Method

	// 读取登录用户信息
	username := c.GetString(conf.Conf.Cache.LoginUserKey)

	switch method {
	case "GET":
		// response_type
		responseType := c.Query("request_type")
		if responseType != "code" {
			_ = c.Error(errors.OtherError("类型错误"))
			return
		}

		// code_challenge
		// code_challenge_method=S256
		codeChallenge := c.Query("code_challenge")
		codeChallengeMethod := c.Query("code_challenge_method")
		if len(codeChallenge) > 0 { // 如果开启了pk模式
			if codeChallengeMethod != "S256" {
				_ = c.Error(errors.InvalidRequestError)
				return
			}
		}

		// id,redirect_uri,scope
		clientId := c.Query("client_id")
		redirectUri := c.Query("redirect_uri")

		// golang中+会被替代为空格
		scopeReq := c.Query("scope") // +,分割,read+writer
		if len(clientId) == 0 && len(redirectUri) == 0 && len(scopeReq) == 0 {
			_ = c.Error(errors.OtherError("缺少重要参数，请构造链接!"))
			return
		}

		// scope_list,+
		scope := strings.Split(scopeReq, " ")

		// 返回授权信息，为认证做准备
		clientId, state, scopes, rUri, err := oauthService.Authorization(username, clientId, "authorization_code", redirectUri, codeChallenge, codeChallengeMethod, scope)

		// index,response_type,redirect_uri才需要返回html，scope验证需要返回错误的范围
		if err != nil {
			if err.Error() == errors.InvalidRequestError.Msg {
				c.Redirect(http.StatusFound, fmt.Sprintf("%v?error=access_denied&error_description=%v", rUri, err.Error()))
				return
			} else {
				_ = c.Error(err)
				return
			}
		}

		// consent，查询到已经记录的，则跳过认证
		if len(clientId) == 0 {
			c.Redirect(http.StatusFound, fmt.Sprintf("%v?code=%v", rUri, state))
			return
		}

		// scope_list
		var scopeList []models.OAuth2Scope
		_ = json.Unmarshal([]byte(scopes), &scopeList)

		// html
		c.HTML(http.StatusOK, "authorize.html", gin.H{
			"client_id": clientId,
			"state":     state,
			"scope":     scopeList,
		})
	case "POST":
		// clientId,state,scope
		clientId := c.PostForm("client_id")
		state := c.PostForm("state")
		scopeReq := c.PostFormArray("scope")
		if len(clientId) == 0 || len(scopeReq) == 0 || len(state) == 0 {
			_ = c.Error(errors.InvalidRequestError)
			return
		}

		// 认证,查询state,username,clientId,验证scope,返回code,redirect_uri
		code, rUri, err := oauthService.Authentication(state, username, scopeReq)

		// 授权的特定错误，需要返回提交给客户端，前面的授权信息查询，返回时，无需返回到错误页面，因为微软也是直接返回错误
		if err != nil {
			c.Redirect(http.StatusFound, fmt.Sprintf("%v?error=access_denied&error_description=%v", rUri, err.Error()))
			return
		}

		// 已经是最后一段了，不需要return
		c.Redirect(http.StatusFound, fmt.Sprintf("%v?code=%v", rUri, code))
	}
}

// Token grant_type=token | refresh_token | client_credentials
func token(c *gin.Context) {

	gType := c.PostForm("grant_type")

	var clientId string
	var clientSecret string
	var codeVerifier string
	var isBasic bool

	// 从basic或者post里获取client的验证
	clientId, clientSecret, isBasic = c.Request.BasicAuth()
	// 或者不走basic验证
	if !isBasic {
		clientId = c.PostForm("client_id")
		clientSecret = c.PostForm("client_secret")
	}

	// pk_ce模式
	codeVerifier = c.PostForm("code_verifier")

	// 验证client_id,必要,code_verifier,client_secret两个参数在service验证
	if len(clientId) == 0 {
		_ = c.Error(errors.InvalidRequestError)
		return
	}

	switch gType {
	case "authorization_code":
		// code,redirect_uri
		code := c.PostForm("code")
		redirectUri := c.PostForm("redirect_uri")
		if len(code) == 0 || len(redirectUri) == 0 {
			_ = c.Error(errors.InvalidRequestError)
			return
		}

		// 查询授权数据后,验证授权数据,生成token
		result, err := oauthService.Token(code, clientId, redirectUri, clientSecret, codeVerifier, c.Request.Host)
		if err != nil {
			_ = c.Error(err)
			return
		}

		c.JSON(http.StatusOK, result)
	case "refresh_token": // 刷新token，按逻辑，如果没有刷新token，则无法使用刷新token
		refreshToken := c.PostForm("refresh_token")
		result, err := oauthService.RefreshToken(clientId, clientSecret, refreshToken, c.Request.Host)
		if err != nil {
			_ = c.Error(err)
			return
		}
		c.JSON(http.StatusOK, result)
	case "client_credentials": // 客户端模式
		result, err := oauthService.ClientCredentials(gType, clientId, clientSecret, c.Request.Host)

		if err != nil {
			_ = c.Error(err)
			return
		}

		c.JSON(http.StatusOK, result)
	default:
		_ = c.Error(errors.OtherError("grant_type is error OAuth2.0"))
		return
	}
}

// Revoke header :token
func revoke(c *gin.Context) {
	// 1.验证client
	clientId, clientSecret, isBasic := c.Request.BasicAuth()
	if !isBasic {
		_ = c.Error(errors.OtherError("认证失败"))
		return
	}
	// 2.获取token
	token := c.PostForm("token")
	tth := c.PostForm("token_type_hint")
	if tth != "access_token" {
		_ = c.Error(errors.OtherError("token不存在"))
		return
	}
	// 3.删除token
	isRevoke, err := oauthService.RevokeToken(clientId, clientSecret, token)
	if err != nil || !isRevoke {
		_ = c.Error(err)
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"msg": "注销成功",
	})
}
